import datetime
from io import BytesIO
import json
from pathlib import Path
from django.shortcuts import render
from django.views.generic import ListView, CreateView, UpdateView, DeleteView
from django.http import HttpResponse, JsonResponse
import openpyxl
from django.db import transaction
from utils.handle_excel import ReadExcel
from utils.permissions import RoleRequiredMixin,role_required
from .models import Student
from .form import StudentForm
from django.contrib.auth.models import User
from django.contrib.auth.hashers import make_password  # 用于加密明文密码
from django.urls import reverse_lazy
from grades.models import Grades
from django.db.models import Q
from teachers.models import Teacher

# Create your views here.

class BaseStudentView(RoleRequiredMixin):
    model = Student
    form_class = StudentForm
    context_object_name = 'students'
    allowed_roles = ['admin','teacher']
    # 需要设置 success_url，否则删除后会报错
    success_url = reverse_lazy('student_list')
    
    def handle_no_permission(self):
        """统一处理未登录/无权限的AJAX请求"""
        if self.request.headers.get('X-Requested-With') == 'XMLHttpRequest':
            return JsonResponse({
                'status': 'error',
                'messages': '登录已过期，请重新登录'
            }, status=401)
        return super().handle_no_permission()

class StudentListView(BaseStudentView,ListView):
    template_name = 'students/student_list.html'
    paginate_by = 10
    
    # 重写 get_queryset 方法，实现搜索功能
    # def get_queryset(self):
    #     queryset = super().get_queryset()  # 保留调用原函数，获取所有的数据内容
    #     search = self.request.GET.get('search')  # 获取搜索的内容，在请求get的URL中获取search参数的值

    #     if search:  # 由于需要多项进行查询，因此使用Q函数
    #         queryset = queryset.filter(
    #             Q(student_name__icontains=search) |
    #             Q(student_number__icontains=search)
    #         )
    #     return queryset
    
    def get_queryset(self):
        queryset = super().get_queryset()
        # 判断是否为老师
        if self.request.session.get('user_role') == 'teacher':
            username = self.request.session.get('username')
            teacher = Teacher.objects.get(teacher_name=username)
            queryset = queryset.filter(grade=teacher.grade)
            
        grade_id = self.request.GET.get('grade') # 获取班级
        keyword = self.request.GET.get('search') # 获取搜索的内容
        if grade_id:
            queryset = queryset.filter(grade__pk=grade_id)
        # 默认 queryset 查询后，下面再进行查询，那么这两个查询就变成联合查询，变成了并且的关系
        if keyword:
            queryset = queryset.filter(
                Q(student_number=keyword) |
                Q(student_name=keyword)
            )
        return queryset
    
    # 重写方法，目的是把数据传到前端页面进行展示
    def get_context_data(self):
        context = super().get_context_data()
        # 如果是老师用户登录
        if self.request.session.get('user_role') == 'teacher':
            username = self.request.session.get('username')
            teacher = Teacher.objects.get(teacher_name=username)
            context['grades'] = Grades.objects.filter(pk=teacher.grade.pk)
        else:
            # 获取所有班级并添加到上下文
            context['grades'] = Grades.objects.all().order_by('grade_number')
        context['current_grade'] = self.request.GET.get('grade', '')
        return context


class StudentCreateView(BaseStudentView,CreateView):
    template_name = 'students/student_form.html'
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 如果是老师用户登录
        if self.request.session.get('user_role') == 'teacher':
            username = self.request.session.get('username')
            teacher = Teacher.objects.get(teacher_name=username)
            context['grades'] = Grades.objects.filter(pk=teacher.grade.pk)
        else:
            context['grades'] = Grades.objects.all().order_by('grade_number')
        return context

    # 重写返回成功的方法
    def form_valid(self, form):
        # 接收字段
        student_name = form.cleaned_data.get('student_name')
        student_number = form.cleaned_data.get('student_number')
        # 写入到auth_user表
        username = student_name + '_' + student_number
        password = student_number[-6:]
        users = User.objects.filter(username=username)
        if users.exists():
            user = users.first()
        else:
            # 创建auth_user表记录
            user = User.objects.create_user(
                username=username, password=password)
        # 写入到student表
        form.instance.user = user
        form.save()

        # 返回json响应
        return JsonResponse({
            'status': 'success',
            'messages': '添加成功'
        }, status=200)

    # 重写返回失败的方法
    def form_invalid(self, form):
        errors = form.errors.as_json()
        return JsonResponse({
            'status': 'error',
            'messages': errors
        }, status=400)


class StudentUpdateView(BaseStudentView,UpdateView):
    template_name = 'students/student_form.html'
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        # 如果是老师用户登录
        if self.request.session.get('user_role') == 'teacher':
            username = self.request.session.get('username')
            teacher = Teacher.objects.get(teacher_name=username)
            context['grades'] = Grades.objects.filter(pk=teacher.grade.pk)
        else:
            context['grades'] = Grades.objects.all().order_by('grade_number')
        return context

    # 验证成功
    def form_valid(self, form):
        # 获取学生对象实例（添加commit=False表示不提交，只是获取student表里所有的字段）
        student = form.save(commit=False)
        # 检查一下是否修改了student_name 和 student_number
        # form.changed_data 获取的是表单修改的字段，如果发生修改，这里返回已被修改的字段
        if 'student_name' or 'student_number' in form.changed_data:
            # 由于student中的user字段与User表进行关联，因此 student.user 就可以访问到User表的字段了
            student.user.username = form.cleaned_data.get(
                'student_name') + '_' + form.cleaned_data.get('student_number')
            # 使用 make_password 把 明文的密码加密后再保存到用户模型中
            student.user.password = make_password(
                form.cleaned_data.get('student_number')[-6:])
            student.user.save()  # 保存更改后的用户模型

        # 保存student模型
        student.save()

        # 返回json响应
        return JsonResponse({
            'status': 'success',
            'messages': '保存成功'
        }, status=200)

    # 验证失败
    def form_invalid(self, form):
        errors = form.errors.as_json()
        return JsonResponse({
            'status': 'error',
            'messages': errors
        }, status=400)


class StudentDeleteView(BaseStudentView,DeleteView):
    # 删除学生信息
    
    # 重写delete方法，前端发送请求的方式必须是DELETE，否则无法接收
    def delete(self, request, *args, **kwargs):
        current_user = request.user.username
        if current_user != 'admin':
            return JsonResponse({'status': 'error','messages': '此为测试管理员，没有删除权限！！！'}, status=400)
        
        # 获取请求的delete对象（前端发送delete请求时返回的对象）
        self.object = self.get_object()
        try:
            # 执行ORM删除操作
            self.object.delete()
            return JsonResponse({
                'status': 'success',
                'messages': '删除成功'
            }, status=200)
        except Exception as e:
            return JsonResponse({
            'status': 'error',
            'messages': '删除失败:' + str(e)
        }, status=500)
        
class StudentBulkDeleteView(BaseStudentView,DeleteView):
    
    # 使用post方法来接收请求
    def post(self, request, *args, **kwargs):
        current_user = request.user.username
        if current_user != 'admin':
            return JsonResponse({'status': 'error','messages': '此为测试管理员，没有删除权限！！！'}, status=400)
        
        # 使用getlist获取，因为student_ids返回的是多个数据
        selected_ids = request.POST.getlist('student_ids')
        if not selected_ids:
            return JsonResponse({
                'status': 'error',
                'messages': '请选择要删除的学生'
            }, status=400)
        
        # 然后在所有数据中筛选出有符合数据
        self.object_list = self.get_queryset().filter(id__in=selected_ids)
        try:
            self.object_list.delete()
            return JsonResponse({
                'status': 'success',
                'messages': '删除成功'
            },status=200)
        except Exception as e:
            return JsonResponse({
                'status': 'error',
                'messages': '删除失败'
            }, status=400)

@transaction.atomic
@role_required('admin', 'teacher')       
def upload_student(request):
    """上传学生信息excel"""
    if request.method == 'POST':
        # request.FILES 接收文件对象 excel_file 是 前台传过来的参数
        file = request.FILES.get('excel_file')
        print(f"file is {file}")
        
        # 判断文件是否上传
        if not file:
            return JsonResponse({
                'status': 'error',
                'messages': '请上传excel文件'
            }, status=400)
            
        # 判断文件类型是否为excel
        ext = Path(file.name).suffix
        if ext.lower() != '.xlsx':
            return JsonResponse({
                'status': 'error',
                'messages': '文件类型错误，请上传.xlsx格式的文件'
            }, status=400)
            
        # openpyxl 读取excel文件内容
        read_excle = ReadExcel(file)
        # get_data 获取所有数据
        data = read_excle.get_data() # [[],[],[],]

        # 规定上传的Excel内容是否符合下面，否则返回错误
        if data[0] != ['班级', '姓名', '学号', '性别', '出生日期', '联系电话', '家庭住址']:
            return JsonResponse({
                'status': 'error',
                'messages': 'Excel中学生信息不是指定格式'
            })
            
        # 写入到数据库
        for row in data[1:]:
            # 使用了拆包（解包），把row分别赋值给每一个变量
            grade, student_name, student_number, gender, birthday, contact_number, address = row 
            # 检查班级是否存在
            grade = Grades.objects.filter(grade_name=grade).first()
            if not grade:
                return JsonResponse({
                    'status': 'error',
                    'messages': f'{grade} 不存在'
                }, status=400)
            # 检测主要字段
            if not student_name:
                return JsonResponse({'status': 'error', 'messages': '学生姓名不能为空'}, status=400)
            if not student_number or len(student_number) != 19:
                return JsonResponse({'status': 'error', 'messages': '学籍号不能为空，并且长度应为19位'}, status=400)
            # 检查日期格式
            if not isinstance(birthday, datetime.datetime) :
                return JsonResponse({'status': 'error', 'messages': '出生日期格式错误'}, status=400)
            # 判断学生信息是否存在
            if Student.objects.filter(student_number=student_number).exists():
                return JsonResponse({'status': 'error', 'messages': f'学号{student_number}已经存在'}, status=400)
            
            # 写入到数据库中
            try:
                # 判断auth_user表中学生数据是否存在，不存在时，在auth_user表中创建用户
                username = student_name + '_' + student_number # 拼接username
                users = User.objects.filter(username=username)
                if users.exists():
                    user = users.first()
                else:
                    password = student_number[-6:]
                    user = User.objects.create_user(username=username, password=password)
                # 在student表中创建记录
                Student.objects.create(
                    student_name = student_name, 
                    student_number = student_number,
                    grade = grade,
                    # gender = 'M' if gender == '男' else 'F',
                    # 由于性别返回的都一样，这里就直接获取了，不用判断 M、F
                    gender = gender,
                    birthday = birthday,
                    contact_number = contact_number,
                    address = address,
                    user = user
                )
            except:
                return JsonResponse({'status': 'error', 'messages': '上传失败'}, status=500)
            
        # 全部写入成功后才提示上传成功
        return JsonResponse({
            'status': 'success',
            'messages': '上传成功'
        }, status=200)

@role_required('admin', 'teacher')
def export_excel(request):
    if request.method == 'POST':
        # 通过 json.loads 把 JSON数据转成字典格式
        data = json.loads(request.body)
        grade_id = data.get('grade')
        # 判断grade_id是否存在
        if not grade_id:
            return JsonResponse({'status': 'error', 'messages': '班级参数缺失'}, status=400)
        # 判断班级是否存在
        try:
            grade = Grades.objects.get(id=grade_id)
        except Grades.DoesNotExist:  # DoesNotExist 判断数据是否不存在
            return JsonResponse({'status': 'error', 'messages': '班级不存在'}, status=404)
        
        # 从数据库查询学生数据
        students = Student.objects.filter(grade=grade)
        if not students.exists():
            return JsonResponse({'status': 'error', 'messages': '找不到指定班级的学生信息'}, status=404)
        
        # 操作excel
        # （由于前面编写的 handle_excel.py 里的 WriteExcel方法是保存在本地；而这里需要通过网络二进制传输到用户那里，因此WriteExcel方法不能使用）
        # 所以这里需要重写
        wb = openpyxl.Workbook()
        ws = wb.active
        # 添加标题行（先写表头）
        columns = ['班级', '姓名', '学号', '性别', '出生日期', '联系电话', '家庭住址']
        ws.append(columns)
        for student in students:
            if student.gender == 'M':
                student.gender = '男'
            elif student.gender == 'F':
                student.gender = '女'
            ws.append([student.grade.grade_name, student.student_name, student.student_number, student.gender, student.birthday, student.contact_number, student.address])

        # 将excel数据保存到内存
        excel_file = BytesIO()
        wb.save(excel_file)
        wb.close()
        # 重置文件指针位置（编写保存后文件的光标指针是在最后，因此需要重置到开头）
        excel_file.seek(0)

        # 设置响应 设置content_type，以二进制的形式进行发送（让其是一个EXCEL的形式）
        response = HttpResponse(excel_file.read(), content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
        # 设置 Content-Disposition，以附件的形式进行发送
        response['Content-Disposition'] = 'attachment; filename="students.xlsx"'
        return response